#include "svm_kernel_classifier.h"

extern bool DEBUG;
extern pn sw;

int ANN=100; // controls noise, high => no noise

void svm_kernel_classifier::train(const vector <int> &ppos, const vector <int> &pneg) {
   pos = ppos;
   neg = pneg;

   wp.resize(pos.size(),1);
   wn.resize(neg.size(),1);

   prob.l = pos.size()+neg.size(); 
   //prob.y.resize(prob.l);
   prob.y = Malloc(double,prob.l);
   prob.x = Malloc(struct svm_node*, prob.l);

   //cout<<prob.l<<" prob.l\n";
   for (int i=0; i<pos.size(); ++i) {
      prob.y[i] = 1;
      prob.x[i] = Malloc(struct svm_node, prob.l+2);
      prob.x[i][0].index = 0;
      prob.x[i][0].value = i+1;
      //cout<<"1 0:"<<i+1<<" ";
      for (int j=0; j<pos.size(); ++j) {
	 prob.x[i][j+1].index = j+1;
	 prob.x[i][j+1].value = sw.kernel(pos[i],pos[j]);
	 //cout<<j+1<<":"<<prob.x[i][j+1].value<<" ";
      }
      for (int j=0; j<neg.size(); ++j) {
	 prob.x[i][j+pos.size()+1].index = j+pos.size()+1;
	 prob.x[i][j+pos.size()+1].value = sw.kernel(pos[i],neg[j]);//findKernel(pos[i].i, neg[j].i);
	 //cout<<j+pos.size()+1<<":"<<prob.x[i][j+pos.size()+1].value<<" ";
      }
      prob.x[i][prob.l+1].index = -1;
      //cout<<"\n";
   }

   for (int i=0; i<neg.size(); ++i) {
      int a=i+pos.size();
      prob.y[a] = -1;
      prob.x[a] = Malloc(struct svm_node, prob.l+2);
      prob.x[a][0].index = 0;
      prob.x[a][0].value = a+1;
      //cout<<"-1 0:"<<a+1<<" ";
      for (int j=0; j<pos.size(); ++j) {
	 prob.x[a][j+1].index = j+1;
	 prob.x[a][j+1].value = sw.kernel(neg[i],pos[j]);//findKernel(neg[i].i, pos[j].i);
	 //cout<<j+1<<":"<<prob.x[a][j+1].value<<" ";
      }
      for (int j=0; j<neg.size(); ++j) {
	 prob.x[a][j+pos.size()+1].index = j+pos.size()+1;
	 prob.x[a][j+pos.size()+1].value = sw.kernel(neg[i],neg[j]);//findKernel(neg[i].i, neg[j].i);
	 //cout<<j+pos.size()+1<<":"<<prob.x[a][j+pos.size()+1].value<<" ";
      }
      prob.x[a][prob.l+1].index = -1;
      //cout<<"\n";
   }

   param.svm_type = C_SVC;
   param.kernel_type = PRECOMPUTED;
   param.degree = 3;
   param.gamma = 0;	// 1/k
   param.coef0 = 0;
   param.nu = 0.5;
   param.cache_size = 100;
   param.C = ANN;//100;
   param.eps = 1e-3;
   param.p = 0.1;
   param.shrinking = 1;
   param.probability = 1; // not default

   param.nr_weight = 0;
   param.weight_label = NULL;
   param.weight = NULL;
  
   /*
   param.nr_weight = 2;
   param.weight_label = Malloc(int,2);
   param.weight = Malloc(double,2);
   param.weight_label[0]=1;
   param.weight_label[1]=-1;
   param.weight[0]= (double)ppos.size()/(ppos.size()+pneg.size());
   param.weight[1]= (double)pneg.size()/(ppos.size()+pneg.size());
*/
   assert(svm_check_parameter(&prob,&param)==NULL);
   struct svm_model *tmodel = svm_train(&prob,&param);
   model = svm_copy_model(tmodel);
   //model = svm_train(&prob,&param);
   //prob.y.clear();
   //free(prob.W);
   
   free(prob.y);
   for (int j=0; j<prob.l; ++j) {
      free(prob.x[j]);
   }
   free(prob.x);

   l = prob.l;

   svm_destroy_model(tmodel);
   trained = true;
}

/*
double svm_kernel_classifier::giveP(const vector <int> &st, const vector <int> &stw) const {
   posNeg pn(st,stw);
   int ind = sw.add(pn);
   return giveP(ind);
}*/


double svm_kernel_classifier::giveP(int ind) const {
   return giveP(sw,ind);
}

double svm_kernel_classifier::giveP(pn&tsw, int ind) const {
   assert(trained);
   if (neg.size()==0 || pos.size()==0) {
      if (pos.size() > 0) return 1; // parameter
      else return 0;
   }
   svm_node *x;
   x = Malloc(struct svm_node, l+2);
   x[0].index = 0;
   for (int i=0; i<pos.size(); ++i) {
      x[i+1].index = i+1;
      x[i+1].value = sw.kernel(tsw,ind,pos[i]);
      //cout<<x[i+1].value<<" ";
   }
   for (int i=0; i<neg.size(); ++i) {
      int a = i+pos.size();
      x[a+1].index = a+1;
      x[a+1].value = sw.kernel(tsw,ind,neg[i]);
      //cout<<x[a+1].value<<" ";
   }
   //cout<<" Test\n";
   x[l+1].index = -1;

   int nr_class=svm_get_nr_class(model);
   double *prob_estimates;
   prob_estimates = (double *) malloc(nr_class*sizeof(double));

   double c = svm_predict_probability(model,x,prob_estimates);
   double r;

   //cout<<c<<" "<<prob_estimates[0]<<" "<<prob_estimates[1]<<"\n";
   if (c==1) {
      assert(prob_estimates[0] >= prob_estimates[1]);
   }
   else {
      assert(prob_estimates[0] <= prob_estimates[1]);
   }
   assert(nr_class==2);

   if (c==1) {
      if (nr_class==2) {
	 r = max(prob_estimates[0], prob_estimates[1]);
      }
      else {
	 r =  prob_estimates[0];
      }
   }
   else {
      if (nr_class==2) {
	 r =  min(prob_estimates[0], prob_estimates[1]);
      }
      else {
	 r =  1-prob_estimates[0];
      }
   }
   free(x);
   free(prob_estimates);
   //if (r==0) {
     // r = 0.000001; 
   //}
   //cout<<r<<" "<<giveDec(s,e)<<"\n";
   // not sure about the following hack
   /*
   double *dec_values;
   dec_values = (double *) malloc((nr_class*(nr_class-1)/2)*sizeof(double));
   svm_predict_values(model,x,dec_values);
   double d;
   if (model->label[0]==1) {
      d = dec_values[0];
   }
   else {
      d = 0-dec_values[0];
   }
   if (d > 0) {
      if (r < 0.5) r = 1-r;
   }
   else {
      if (r > 0.5) r = 1-r;
   }
   */
   return r;
}

double svm_kernel_classifier::giveDec(int u) const {
   return giveDec(sw,u);
}

double svm_kernel_classifier::giveDec(pn &tsw, int u) const {
   // gives decision value
   assert(trained);
   
   svm_node *x;
   x = Malloc(struct svm_node, l+2);
   x[0].index = 0;
   for (int i=0; i<pos.size(); ++i) {
      x[i+1].index = i+1;
      x[i+1].value = sw.kernel(tsw,u,pos[i]);
      //cout<<x[i+1].value<<" ";
   }
   for (int i=0; i<neg.size(); ++i) {
      int a = i+pos.size();
      x[a+1].index = a+1;
      x[a+1].value = sw.kernel(tsw,u,neg[i]);
      //cout<<x[a+1].value<<" ";
   }
   //cout<<" Test\n";
   x[l+1].index = -1;

   int nr_class=svm_get_nr_class(model);
   assert(nr_class==2);
   double *dec_values;
   dec_values = (double *) malloc((nr_class*(nr_class-1)/2)*sizeof(double));

   double r;
   
   if (model->label[0]==1) {
      r = dec_values[0];
   }
   else {
      r = 0-dec_values[0];
   }
   
   free(x);
   free(dec_values);

   return r;
}
